package com.husd.framework.autocode;

import com.husd.framework.autocode.dto.EbayWord;
import com.husd.framework.autocode.dto.XmlDefine;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author shengdonghu@126.com
 * @date 2023/4/23 10:22
 */
public class Ebay02XmlConvert {

    Stack<XmlDefine> ebayWordStack = new Stack<>();

    // root元素我们认为是第一层
    // 其它子元素 我们依次向下增加
    // 开始解析当前元素 如果当前元素没有

    public List<XmlDefine> convert(List<EbayWord> ebayWordList) {

        preDealRepeat(ebayWordList);
        final int len = ebayWordList.size();
        // 解析分好词的数据 转换为xml结构的数据
        List<XmlDefine> res = new ArrayList<>();
        // 控制属性的转换状态
        boolean attr = false;
        for (int i = 0; i < ebayWordList.size(); i++) {
            EbayWord preEbayWord = getPre(ebayWordList, len, i);
            EbayWord currentEbayWord = getCurrent(ebayWordList, len, i);
            EbayWord nextWord = getNext(ebayWordList, len, i);
            switch (currentEbayWord.getType()) {
                case L1:
                    // 分2种情况 一种是 < 一种是 </
                    if (nextWord.getType() == EbayWord.EbayWorkType.XIE) {
                        // 忽略就行了 </
                        // 把栈的数据弹出 然后写入结果
                        // XmlDefine xmlDefine = ebayWordStack.pop();

                    } else {
                        // 这种就是标签的开始，需要把数据解析出来，放到栈里
                        XmlDefine xmlDefine = new XmlDefine();
                        // 需要先把元素的类型确定了
                        ebayWordStack.push(xmlDefine);
                    }
                    break;
                case R1:
                    // > 表示标签结尾 这里需要判断一下下一个元素是否是可以重复的
//                    EbayWord nextEbayWord = getNext(ebayWordList, len, i);
//                    if(nextEbayWord != null &&
//                            nextEbayWord.getType() == EbayWord.EbayWorkType.C1) {
//                        // 下一个元素是注释
//                        XmlDefine nextXmlDefineC = ebayWordStack.pop();
//                        EbayWord nextnextEbayWord = getNext(ebayWordList,len,i+1);
//                        String c = findMoreName(nextnextEbayWord.getName());
//                        if (nextnextEbayWord != null &&
//                                StringUtils.equalsIgnoreCase(nextnextEbayWord.getName(), c)) {
//                            nextXmlDefineC.setRepeat(true);
//                        }
//                        ebayWordStack.push(nextXmlDefineC);
//                    }
                    break;
                case WORD:
                    // 0异常情况 1 元素名字开始 6元素名字的结束 2 注释 3 元素的值 4 属性的名字 5 属性的值
                    int wordType = findWordType(ebayWordList, len, i);
                    XmlDefine xmlDefine = ebayWordStack.pop();
                    switch (wordType) {
                        case 1:
                            String _ele_name = currentEbayWord.getName();
                            // 简单处理命名空间
                            if(_ele_name.contains(":")) {
                                String [] _arr = _ele_name.split(":");
                                _ele_name = _arr[1];
                            }
                            xmlDefine.setName(_ele_name);
                            xmlDefine.setRepeat(currentEbayWord.isRepeat());
                            ebayWordStack.push(xmlDefine);
                            break;
                        case 6:
                            // 这种就是标签的结束 需要把站里的数据拿出来 表示结束了
                            // 这个时候就需要把栈里的上一个元素，作为父亲元素
                            // 如果栈里没有元素了，那么他自己就是root元素
                            // 这段逻辑是走 标签结束的逻辑
                            endElement(res, xmlDefine, currentEbayWord);
                            break;
                        case 2:
                            ebayWordStack.push(xmlDefine);
                            break;
                        case 3:
                            // 元素的值，需要向前一直找，直到找到 < 为止
                            StringBuilder stringBuilder = new StringBuilder();
                            stringBuilder.append(currentEbayWord.getName());
                            EbayWord next = getNext(ebayWordList, len, i);
                            int offset = 0;
                            while (next != EbayWord.L1) {
                                stringBuilder.append(" " + next.getName());
                                offset++;
                                next = getNext(ebayWordList, len, i + offset);
                            }
                            xmlDefine.setValue(stringBuilder.toString());
                            ebayWordStack.push(xmlDefine);
                            i = i + offset;
                            break;
                        case 4:
                            // 属性的名字的话，需要新增一个
                            List<XmlDefine.XmlAttrDefine> attrList = xmlDefine.getAttrList();
                            if (attrList == null) {
                                attrList = new ArrayList<>();
                            }
                            XmlDefine.XmlAttrDefine xmlAttrDefine = new XmlDefine.XmlAttrDefine();
                            xmlAttrDefine.setName(currentEbayWord.getName());
                            attrList.add(xmlAttrDefine);
                            xmlDefine.setAttrList(attrList);
                            ebayWordStack.push(xmlDefine);
                            break;
                        case 5:
                            // 属性的值的话，用列表的最后一个元素填充
                            System.out.println("遇到了属性的值 不应该出现" + currentEbayWord.getName());
                    }
                    break;
                case EQ:
                    break;
                case QUO:
                    attr = !attr;
                    if (attr) {
                        xmlDefine = ebayWordStack.pop();
                        // 表示遇到了第一个 " 这个时候就是遇到属性的val了
                        StringBuilder stringBuilder = new StringBuilder();
                        //stringBuilder.append(ebayWord.getName());
                        EbayWord next = getNext(ebayWordList, len, i);
                        int offset = 0;
                        while (next != EbayWord.QUO) {
                            stringBuilder.append(" " + next.getName());
                            offset++;
                            next = getNext(ebayWordList, len, i + offset);
                        }
                        List<XmlDefine.XmlAttrDefine> attrList2 = xmlDefine.getAttrList();
                        XmlDefine.XmlAttrDefine xmlAttrDefine2 = attrList2.get(attrList2.size() - 1);
                        xmlAttrDefine2.setValue(stringBuilder.toString());
                        xmlDefine.setAttrList(attrList2);
                        ebayWordStack.push(xmlDefine);

                        // 把index调整到 i+offset
                        i = i + offset;
                    }
                    break;
                case XIE:
                    //  <ProductEan Ean="" />
                    // 这种情况 如果是 /> 就表示结束了
                    if (nextWord.getType() == EbayWord.EbayWorkType.R1 && (
                            preEbayWord.getType() == EbayWord.EbayWorkType.WORD ||
                                    preEbayWord.getType() == EbayWord.EbayWorkType.QUO
                    )) {

                        xmlDefine = ebayWordStack.pop();
                        // 这段逻辑是走 标签结束的逻辑
                        endElement(res, xmlDefine, currentEbayWord);
                        break;

                    }
                    break;
                case C1:
                    // 发现了注释 有3个元素
                    // <!--
                    // 注释的内容
                    // -->
                    // 跳过去内容
//                    System.out.println("跳过去了的注释内容如下:");
//                    System.out.println(currentEbayWord.toString());
//                    System.out.println(getNext(ebayWordList, len, i));
//                    System.out.println(getNext(ebayWordList, len, i + 1));
                    i = i + 2;
                    break;
                case C2:
                    break;
                default:
                    throw new RuntimeException("不识别的元素类型 name: " + currentEbayWord.getName());
            }
        }
        if (ebayWordStack.isEmpty() == false) {
            //System.out.println(JSON.toJSONString(ebayWordStack));
            throw new RuntimeException("循环状态 但是栈里还有数据 异常结束: " + ebayWordStack.size());
        }

        Stack<XmlDefine> ss = new Stack<>();
        for (XmlDefine xmlDefine : res) {
            ss.add(xmlDefine);
        }

        while (ss.isEmpty() == false) {

            XmlDefine xmlDefine = ss.pop();
            setXmlPath(xmlDefine);
            List<XmlDefine> elementList = xmlDefine.getElementList();
            if (CollectionUtils.isNotEmpty(elementList)) {
                for (XmlDefine xx : elementList) {
                    ss.add(xx);
                }
            }
        }
        return res;
    }

    private void setXmlPath(XmlDefine xmlDefine) {

        StringBuilder sb = new StringBuilder();
        List<String> list = new ArrayList<>();
        list.add(xmlDefine.getName());
        XmlDefine current = xmlDefine;
        while (current.getParentXmlDefine() != null) {
            list.add(current.getParentXmlDefine().getName());
            current = current.getParentXmlDefine();
        }
        Collections.reverse(list);
        for (int i = 0; i < list.size(); i++) {
            sb.append(list.get(i));
            if (i != (list.size() - 1)) {
                sb.append(":");
            }
        }
        String path = sb.toString();
        xmlDefine.setPath(path);
    }

    private void endElement(List<XmlDefine> res, XmlDefine xmlDefine, EbayWord currentEbayWord) {

        XmlDefine pre = ebayWordStack.isEmpty() ? null : ebayWordStack.pop();
        if (pre == null) {
            setElementType(xmlDefine);
            xmlDefine.setRepeat(currentEbayWord.isRepeat());
            res.add(xmlDefine);
        } else {
            // current是pre的子节点
            addXmlDefine2ElementList(pre, xmlDefine, currentEbayWord);
            // 要把pre也要放回去
            ebayWordStack.push(pre);
        }
    }

    /**
     * 这个方法 需要判断这些加进去的新增元素 是不是和之前的元素是同一个类型的
     * 如果是同一个类型的 那么就需要认为是数组
     *
     * @param pre
     * @param xmlDefine
     * @param currentEbayWord
     */
    private void addXmlDefine2ElementList(XmlDefine pre, XmlDefine xmlDefine, EbayWord currentEbayWord) {

        List<XmlDefine> elementList = pre.getElementList();
        setElementType(xmlDefine);
        xmlDefine.setParentXmlDefine(pre);
        if (elementList == null) {
            elementList = new ArrayList<>();
            xmlDefine.setRepeat(currentEbayWord.isRepeat());
            elementList.add(xmlDefine);
        } else {
            // 这里可能会发生重复
            XmlDefine sameXmlDefine = findSameXmlDefine(elementList, xmlDefine);
            boolean repeat = sameXmlDefine != null; //
            if (repeat) {
                // 重复了 不需要新增 只需要标识重复即可
                sameXmlDefine.setRepeat(true);
            } else {
                xmlDefine.setRepeat(currentEbayWord.isRepeat());
                elementList.add(xmlDefine);
            }
        }

        pre.setElementList(elementList);
    }

    private XmlDefine findSameXmlDefine(List<XmlDefine> elementList, XmlDefine xmlDefine) {

        for (XmlDefine item : elementList) {
            if (sameXmlDefine2(item, xmlDefine)) {
                return item;
            }
        }
        return null;
    }

    // 宽松模式比较 只需要名字一样就行了 不比较元素和属性
    private boolean sameXmlDefine2(XmlDefine d1, XmlDefine d2) {

        if (d1 == d2) return true;
        return StringUtils.equalsIgnoreCase(d1.getName(), d2.getName());
    }

    // 严格模式比较 必须要 名字一致 且 属性一致 且 子元素一致
    private boolean sameXmlDefine(XmlDefine d1, XmlDefine d2) {

        if (d1 == d2) return true;
        return Objects.equals(d1.getName(), d2.getName()) && d1.getType() == d2.getType() && sameXmlDefineAttr(d1.getAttrList(), d2.getAttrList()) && sameXmlDefineElementList(d1.getElementList(), d2.getElementList());
    }

    private boolean sameXmlDefineElementList(List<XmlDefine> elementList1, List<XmlDefine> elementList2) {

        if (CollectionUtils.isEmpty(elementList1) && CollectionUtils.isEmpty(elementList2)) {
            return true;
        }

        if (CollectionUtils.isEmpty(elementList1) || CollectionUtils.isEmpty(elementList2)) {
            return false;
        }

        if (elementList1.size() != elementList2.size()) {
            return false;
        }

        elementList1.sort(Comparator.comparing(XmlDefine::hashCode));
        elementList2.sort(Comparator.comparing(XmlDefine::hashCode));

        int len = elementList1.size();
        for (int i = 0; i < len; i++) {
            if (!sameXmlDefine(elementList1.get(i), elementList2.get(i))) {
                return false;
            }
        }
        return true;
    }

    private boolean sameXmlDefineAttr(List<XmlDefine.XmlAttrDefine> attrList1, List<XmlDefine.XmlAttrDefine> attrList2) {

        if (CollectionUtils.isEmpty(attrList1) && CollectionUtils.isEmpty(attrList2)) {
            return true;
        }

        if (CollectionUtils.isEmpty(attrList1) || CollectionUtils.isEmpty(attrList2)) {
            return false;
        }

        if (attrList1.size() != attrList2.size()) {
            return false;
        }
        List<String> name1 = attrList1.stream().map(a -> a.getName()).collect(Collectors.toList());
        List<String> name2 = attrList2.stream().map(a -> a.getName()).collect(Collectors.toList());

        name1.sort(Comparator.comparing(String::hashCode));
        name2.sort(Comparator.comparing(String::hashCode));

        int len = name1.size();
        for (int i = 0; i < len; i++) {
            if (!StringUtils.equals(name1.get(i), name2.get(i))) {
                return false;
            }
        }
        return true;
    }

    private void preDealRepeat(List<EbayWord> ebayWordList) {

        // 提前处理一下标签是否可以重复
        int len = ebayWordList.size();
        for (int i = 2; i < len - 4; i++) {
            EbayWord current = ebayWordList.get(i);
            EbayWord pre = ebayWordList.get(i - 1);
            EbayWord next = ebayWordList.get(i + 1);

            //         <Weight>100</Weight>
            //        <!-- ... more Weight nodes allowed here ... -->
            if (current.getType() == EbayWord.EbayWorkType.C1 && pre.getType() == EbayWord.EbayWorkType.R1) {
                EbayWord comment = ebayWordList.get(i + 1);
                boolean meaning = meaning(comment);
                if (meaning) {
                    // 判断前一个是不是匹配
                    EbayWord prepre = ebayWordList.get(i - 2);
                    String moreName = findMoreName(comment.getName());
                    if (StringUtils.equalsIgnoreCase(moreName, prepre.getName())) {
                        prepre.setRepeat(true);
                        //EbayWord prepreMember = ebayWordList.get(i - 2 - 2);
                        //prepreMember.setRepeat(true);
                    }

                    // 判断下一个是不是匹配
                    EbayWord nextnext = ebayWordList.get(i + 4);
                    if (StringUtils.equalsIgnoreCase(moreName, nextnext.getName())) {
                        nextnext.setRepeat(true);
                        //EbayWord nextnextMember = ebayWordList.get(i + 4 +2);
                        //nextnextMember.setRepeat(true);
                    }
                }
            }
        }
    }

    private boolean meaning(EbayWord ebayWord) {

        return ebayWord != null && StringUtils.isNotBlank(ebayWord.getName()) && ebayWord.getName().contains("more") && ebayWord.getName().contains("allowed here");
    }

    private String findMoreName(String name) {

        // <!-- ... more CrossBorderTrade values allowed here ... -->
        String[] arr = name.split(" ");
        for (int i = 1; i < arr.length - 1; i++) {
            String current = arr[i];
            String pre = arr[i - 1];
            String next = arr[i + 1];
            if (StringUtils.equalsIgnoreCase(pre, "more") || StringUtils.equalsIgnoreCase(next, "values")) {
                return current;
            }
        }
        return null;
    }

    private void setElementType(XmlDefine xmlDefine) {

        List<XmlDefine> elementList = xmlDefine.getElementList();
        List<XmlDefine.XmlAttrDefine> attrList = xmlDefine.getAttrList();

        // 只要有元素或者属性 那么就需要是复合类型
        if (CollectionUtils.isNotEmpty(elementList) || CollectionUtils.isNotEmpty(attrList)) {
            xmlDefine.setType(XmlDefine.XmlDefineType.COMPLEX);
        } else {
            String value = xmlDefine.getValue();
            if (StringUtils.isBlank(value)) {
                value = "";
            }
            switch (value) {
                case "string":
                    xmlDefine.setType(XmlDefine.XmlDefineType.STRING);
                    break;
                case "boolean":
                    xmlDefine.setType(XmlDefine.XmlDefineType.BOOLEAN);
                    break;
                case "int":
                    xmlDefine.setType(XmlDefine.XmlDefineType.INT);
                    break;
                case "long":
                    xmlDefine.setType(XmlDefine.XmlDefineType.LONG);
                    break;
                case "time":
                    xmlDefine.setType(XmlDefine.XmlDefineType.TIME);
                    break;
                case "DaysCodeType":
                    xmlDefine.setType(XmlDefine.XmlDefineType.DAYSCODETYPE);
                    break;
                case "token":
                    xmlDefine.setType(XmlDefine.XmlDefineType.STRING);
                    break;
                case "float":
                    xmlDefine.setType(XmlDefine.XmlDefineType.FLOAT);
                    break;
                default:
                    xmlDefine.setType(XmlDefine.XmlDefineType.STRING);
            }
        }

    }

    private int findWordType(List<EbayWord> ebayWordList, int len, int index) {
        // 0异常情况 1 元素名字开始 6元素名字结束 2 注释 3 元素的值 4 属性的名字 5 属性的值
        EbayWord current = getCurrent(ebayWordList, len, index);
        EbayWord pre = getPre(ebayWordList, len, index);
        String val = current.getName();
        if (pre == null) {
            throw new RuntimeException("wordtype 没有找到 name: " + current.getName());
        }
        EbayWord next = getNext(ebayWordList, len, index);
        EbayWord nextnext = getNext(ebayWordList, len, index + 1);
        // <Item
        if (pre.getType() == EbayWord.EbayWorkType.L1) {
            return 1;
        } else if (pre.getType() == EbayWord.EbayWorkType.R1) {
            EbayWord _current = current;
            EbayWord _next = next;
            EbayWord _nextnext = nextnext;
            int i = 0;
            while(_current.getType() == EbayWord.EbayWorkType.WORD &&
                    _next.getType() == EbayWord.EbayWorkType.WORD) {
                _current = getCurrent(ebayWordList, len, index + i);
                _next = getNext(ebayWordList, len, index + i);
                _nextnext = getNext(ebayWordList, len, index + i + 1);
                i++;
            }
            // 分2种情况
            if (_next != null
                    && _next.getType() == EbayWord.EbayWorkType.L1
                    && _nextnext != null
                    && _nextnext.getType() == EbayWord.EbayWorkType.XIE) {
                // > abc </xxx>
                // > abc (double) adb </xxx>
                return 3;
            } else if (_next != null
                    && _next.getType() == EbayWord.EbayWorkType.L1
                    && _nextnext != null
                    && _nextnext.getType() == EbayWord.EbayWorkType.WORD) {
                // > abc
                // <xxx>
                return 2;
            }  else {
                // <name> this is a name </name>
                // 当前的type是一个word 我们要判断移动到最后最后一个word 判断
                error("findWordType pre == > 没有找到类型", current, pre, next);
            }
        } else if (pre.getType() == EbayWord.EbayWorkType.WORD) {
            // <BuyItNowPrice currencyID
            return 4;
        } else if (pre.getType() == EbayWord.EbayWorkType.EQ) {
            return 5;
        } else if ((pre.getType() == EbayWord.EbayWorkType.XIE && next.getType() == EbayWord.EbayWorkType.R1)) {
            // <xxx> </xxx> 或者 <xxx a="10" />
            return 6;
        } else if (pre.getType() == EbayWord.EbayWorkType.XIE) {

            if (current.getType() == EbayWord.EbayWorkType.R1) {
                return 6;
            } else {
                error("findWordType pre == / 没有找到类型", current, pre, next);
            }
        }  else if(pre.getType() == EbayWord.EbayWorkType.C1) {
            if(next.getType() == EbayWord.EbayWorkType.C2) {
                return 2;
            } else {
                error("findWordType pre == / 没有找到类型", current, pre, next);
            }
        }

        if (next != null && next.getType() == EbayWord.EbayWorkType.WORD) {
            return 3;
        }

        if (pre != null && pre.getType() == EbayWord.EbayWorkType.QUO && next != null && next.getType() == EbayWord.EbayWorkType.EQ) {
            return 4;
        }
        error("wordtype 没有找到 name: ", current, pre, next);
        return -11;
    }

    private void error(String msg, EbayWord current, EbayWord pre, EbayWord next) {

        throw new RuntimeException(msg + " pre:" + pre.getName() + " current:" + current.getName() + " next:" + next.getName());
    }

    private EbayWord getNext(List<EbayWord> ebayWordList, int len, int index) {

        int nextIndex = index + 1;
        if (nextIndex >= len) {
            return null;
        }
        return ebayWordList.get(nextIndex);
    }

    private EbayWord getPre(List<EbayWord> ebayWordList, int len, int index) {

        int preIndex = index - 1;
        if (preIndex >= len) {
            return null;
        }
        if (preIndex < 0) {
            return null;
        }
        return ebayWordList.get(preIndex);
    }

    private EbayWord getCurrent(List<EbayWord> ebayWordList, int len, int index) {

        if (index >= len) {
            return null;
        }
        if (index < 0) {
            return null;
        }
        return ebayWordList.get(index);
    }
}
